﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using Red.Controls.Curve.ViewModels;
using Red.Controls.TimeLine.Core;
using Red.Controls.ViewModels;

namespace Red.Controls.Curve
{
    internal sealed class InterpolationTypeExtension : MarkupExtension
    {
        public InterpolationTypeExtension( int value )
        {
            Value = (EInteroplationType)value;
        }

        public EInteroplationType Value { get; set; }
        public override Object ProvideValue( IServiceProvider sp )
        {
            return Value;
        }
    };

    internal sealed class InterpolationTypeConverter : IValueConverter
    {
        #region IValueConverter implementation

        public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
        {
            EInteroplationType val = (EInteroplationType)value;
            EInteroplationType param = (EInteroplationType)parameter;
            return val == param;
        }

        public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
        {
            throw new NotImplementedException();
        }

        #endregion IValueConverter implementation
    }

    public partial class CurveEditor : UserControl
    {
        #region Data

        private ObservableCollection<CurveControl> _curves;
        private CurveControl _mainCurve = null;

        private Point _cachedMousePos;

        private readonly TimeLineControler _timeController = new TimeLineControler( 1, 255 );
        private readonly TimeLineControler _valuesController = new TimeLineControler( 1, 255 );

        #endregion

        public static readonly DependencyProperty ActiveCurveProperty = DependencyProperty.Register( "ActiveCurve",
               typeof( CurveControl ), typeof( CurveEditor ), new FrameworkPropertyMetadata( null ) );

        public CurveControl ActiveCurve
        {
            get { return (CurveControl)GetValue( ActiveCurveProperty ); }
            set { SetValue( ActiveCurveProperty, value ); }
        }

        #region C-tor

        public CurveEditor()
        {
            _curves = new ObservableCollection<CurveControl>();
            _curves.CollectionChanged += Curves_CollectionChanged;

            DataContextChanged += CurveEditor_DataContextChanged;
            InitializeComponent();

            _curvesList.ItemsSource = _curves;
            _timeAxis.TimeLineControler = _timeController;
            _valueAxis.TimeLineControler = _valuesController;

            _timeController.StateChanged += Controllers_StateChanged;
            _valuesController.StateChanged += Controllers_StateChanged;
        }

        #endregion

        #region Public methods
        public void Save()
        {
            foreach ( var curve in _curves )
            {
                curve.Save();
            }
        }
        public void RevertChanges()
        {
            foreach ( var curve in _curves )
            {
                curve.RevertChanges();
            }
        }

        #endregion Public methods

        #region Event handlers

        private void CurveEditor_DataContextChanged( object sender, DependencyPropertyChangedEventArgs e )
        {
            if ( _mainCurve != null )
            {
                _curves.Remove( _mainCurve );
                _mainCurve = null;
            }

            var dc = e.NewValue as DataItem;
            if ( dc != null )
            {
                _mainCurve = new CurveControl( _canvas, _timeAxis, _valueAxis );
                _mainCurve.DataContext = dc;
                _curves.Add( _mainCurve );
            }
        }

        private void Controllers_StateChanged( object sender, EventArgs e )
        {
            foreach ( var curve in _curves )
            {
                curve.Rearrange();
            }
        }

        private void Curves_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
        {
            var allAdorners = AdornerLayer.GetAdornerLayer( _canvas ).GetAdorners( _canvas );
            if ( e.Action == NotifyCollectionChangedAction.Reset )
            {
                if ( allAdorners != null )
                {
                    foreach ( var adorner in allAdorners )
                    {
                        AdornerLayer.GetAdornerLayer( _canvas ).Remove( adorner );
                    }
                }

                ActiveCurve = null;
                _mainCurve = null;
                _curves.Clear();
            }

            if ( e.NewItems != null )
            {
                foreach ( var item in e.NewItems )
                {
                    CurveControl cc = item as CurveControl;
                    cc.OnActivation = curve => ActiveCurve = curve;

                    if ( IsVisible == true )
                    {
                        cc.Show();
                    }
                }
            }

            if ( e.OldItems != null )
            {
                foreach ( var item in e.OldItems )
                {
                    ( item as CurveControl ).Hide();
                }
            }
        }

        private void UserControl_IsVisibleChanged( object sender, DependencyPropertyChangedEventArgs e )
        {
            bool? newVal = e.NewValue as bool?;
            if ( newVal != null )
            {
                if ( newVal.Value == true )
                {
                    foreach ( var curve in _curves )
                    {
                        curve.Show();
                    }
                }
                else
                {
                    foreach ( var curve in _curves )
                    {
                        curve.Hide();
                    }
                }
            }
        }

        private void Canvas_MouseLeftButtonDown( object sender, MouseButtonEventArgs e )
        {
            if ( e.ClickCount == 2 )
            {
                Point clickPoint = e.GetPosition( _canvas );

                if ( ActiveCurve != null )
                {
                    ActiveCurve.AddPoint( clickPoint );
                }
            }
        }

        private void RemovePoint_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            // todo: pass only time as command parameter
            CircleControl circle = e.Parameter as CircleControl;
            if ( circle != null )
            {
                ActiveCurve.RemovePoint( circle.SourcePoint.Time );
            }
        }
        
        private void VisibilityButton_Checked( object sender, RoutedEventArgs e )
        {
            var tc = ( sender as RedToggleButton ).DataContext as TrackControl;
            AdornerLayer.GetAdornerLayer( _canvas ).Remove( tc );
        }

        private void VisibilityButton_Unchecked( object sender, RoutedEventArgs e )
        {
            var tc = ( sender as RedToggleButton ).DataContext as TrackControl;
            AdornerLayer.GetAdornerLayer( _canvas ).Add( tc );
        }
        private void LockButton_Checked( object sender, RoutedEventArgs e )
        {
            var tc = ( sender as RedToggleButton ).DataContext as TrackControl;
            tc.Locked = true;
        }

        private void LockButton_Unchecked( object sender, RoutedEventArgs e )
        {
            var tc = ( sender as RedToggleButton ).DataContext as TrackControl;
            tc.Locked = false;
        }

        private void StackPanel_PreviewMouseDown( object sender, MouseButtonEventArgs e )
        {
            var obj = sender as FrameworkElement;
            
            if ( obj.DataContext as CurveControl != null )
            {
                ( obj as CurveControl ).Activate( null );
            }
            else if ( obj.DataContext as TrackControl != null )
            {
                var track = obj.DataContext as TrackControl;
                track.ParentCurve.Activate( track );
            }
        }

        private void CurvesList_SelectedItemChanged( object sender, RoutedPropertyChangedEventArgs<object> e )
        {
            if ( e.NewValue as CurveControl != null )
            {
                (e.NewValue as CurveControl).Activate( null );
            }
            else if ( e.NewValue as TrackControl != null )
            {
                var track = e.NewValue as TrackControl;
                track.ParentCurve.Activate( track );
            }
        }

        private void Canvas_MouseRightButtonDown( object sender, MouseButtonEventArgs e )
        {
            _cachedMousePos = e.GetPosition( _canvas );
            _canvas.CaptureMouse();
        }

        private void Canvas_MouseRightButtonUp( object sender, MouseButtonEventArgs e )
        {
            _canvas.ReleaseMouseCapture();
        }

        private void Canvas_MouseMove( object sender, MouseEventArgs e )
        {
            if ( _canvas.IsMouseCaptured && e.RightButton == MouseButtonState.Pressed )
            {
                Point p = e.GetPosition( _canvas );
                Vector delta = p - _cachedMousePos;
                _cachedMousePos = p;

                _timeController.MoveStartTime( -delta.X / _timeController.IntervalSize );
                _valuesController.MoveStartTime( delta.Y / _valuesController.IntervalSize );
            }
        }

        private void Canvas_MouseWheel( object sender, MouseWheelEventArgs e )
        {
            _timeController.Scale( e.Delta );
            _valuesController.Scale( e.Delta );
        }

        private void CurveTypeChanged_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            EInteroplationType newType = (EInteroplationType)e.Parameter;

            if ( ActiveCurve != null )
            {
                ActiveCurve.InterpolationType = newType;
            }
        }

        #endregion Event handlers

        #region Private methods

        private void AddCurveControl( DataItem curveDataItem )
        {
            var curve = _curves.FirstOrDefault( el => el.DataContext == curveDataItem );
            if ( curve != null )
            {
                return;
            }

            _curves.Add( new CurveControl( _canvas, _timeAxis, _valueAxis ) );
            _curves.Last().DataContext = curveDataItem;
        }

        private void RemoveCurveControl( DataItem curveDataItem )
        {
            var curveToRemove = _curves.FirstOrDefault( curve => curve.DataContext == curveDataItem );
            if ( curveToRemove != null )
            {
                _curves.Remove( curveToRemove );
            }
        }

        #endregion Private methods
    }
}